package com.bestv.search.engine.command;

import com.bestv.search.common.cache.SystemCache;
import com.bestv.search.common.dataSource.DataSourceKey;
import com.bestv.search.common.dataSource.DataSourceKeyUtil;
import com.bestv.search.common.model.*;
import com.bestv.search.common.service.InfoManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.compass.core.*;
import org.compass.core.CompassQueryBuilder.CompassBooleanQueryBuilder;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class SimilarSearchCommand extends AbstractSearchCommand {
    protected final Log log = LogFactory.getLog(getClass());
    private InfoManager infoManager;
    private SystemCache systemCache;

    public InfoManager getInfoManager() {
        return infoManager;
    }

    public void setInfoManager(InfoManager infoManager) {
        this.infoManager = infoManager;
    }

    public String similarSearch(String jsonStr) throws JSONException {
        String result = "";
        List<String> keywords = new ArrayList<String>();
        List<String> fields = new ArrayList<String>();
        List<String> types = new ArrayList<String>();
        List<String> subtypes = new ArrayList<String>();
        JSONObject jo = new JSONObject(jsonStr);
        JSONArray array;
        try {
            array = jo.getJSONArray("fields");
            for (int i = 0; i < array.length(); ++i)
                fields.add(array.getJSONObject(i).getString("field"));
        } catch (JSONException e) {
            fields = null;
        }

        try {
            array = jo.getJSONArray("types");
            for (int i = 0; i < array.length(); ++i)
                types.add(array.getJSONObject(i).getString("type"));
        } catch (JSONException e) {
            types = null;
        }

        try {
            array = jo.getJSONArray("subtypes");
            for (int i = 0; i < array.length(); ++i)
                subtypes.add(array.getJSONObject(i).getString("subtype"));
        } catch (JSONException e) {
            subtypes = null;
        }
        // 取关键字
        String code = jo.getString("mediacode");
        log.debug(code);
        code.replaceAll(":", "\\\\:").replaceAll("[", "\\\\[").replaceAll("]", "\\\\]").replaceAll("(", "\\\\(")
                .replaceAll(")", "\\\\(").replaceAll("*", "\\\\*").replaceAll("?", "\\\\?").replaceAll("\\", "\\\\");

        DataSourceKey key = DataSourceKeyUtil.convert(systemCache.getLocalDataSourceKey());
        List<Info> infos = infoManager.getInfoByCode(key, code);
        Info info = null;
        if (infos == null || infos.size() == 0)
            return "[error]";
        else
            info = infos.get(0);

        keywords.add(info.getName().replaceAll("[1-9]", "").replaceAll("[\\[\\]]", ""));
        // if(info.getTags() != null && !"".equals(info.getTags()))
        // {
        // String[] tags = info.getTags().split(" ");
        // for(String tag: tags)
        // keywords.add(tag);
        // }
        Set<ManualBind> manuals = info.getManualBinds();
        for (ManualBind mb : manuals) {
            if (mb.getTags() != null && !"".equals(mb.getTags())) {
                String[] manualtags = mb.getTags().split(" ");
                for (String manualtag : manualtags)
                    keywords.add(manualtag);
            }
        }
        if (info.getGenre() != null && !"".equals(info.getGenre()))
            keywords.add(info.getGenre());
        String actors = "";
        String directors = "";
        if ("节目".equals(info.getType().getName())) {
            Program p = (Program) info;
            actors = p.getActors();
            directors = p.getDirectors();
        } else if ("连续剧".equals(info.getType().getName())) {
            Series s = (Series) info;
            actors = s.getActors();
            directors = s.getDirectors();
        } else if ("节目单".equals(info.getType().getName())) {
            Schedule sh = (Schedule) info;
            actors = sh.getActors();
            directors = sh.getDirectors();
        }
        if (actors != null && !"".equals(actors)) {
            String[] ats = actors.split(" ");
            for (String at : ats)
                keywords.add(at);
        }
        if (directors != null && !"".equals(directors)) {
            String[] dts = directors.split(" ");
            for (String dt : dts)
                keywords.add(dt);
        }
        log.debug("keywords: " + keywords);
        log.debug("type: " + types);
        log.debug("subtype: " + subtypes);
        log.debug("fields: " + fields);
        result = search(keywords, fields, types, subtypes);
        return result;
    }

    public String search(final List<String> keywords, final List<String> fields, final List<String> types,
                         final List<String> subtypes) {
        return getCompassTemplate().execute(new CompassCallback<String>() {
            public String doInCompass(CompassSession session) throws CompassException {
                return performSearch(session, keywords, fields, types, subtypes);
            }
        });
    }

    protected String performSearch(CompassSession session, List<String> keywords, List<String> fields,
                                   List<String> types, List<String> subtypes) {
        String result = "";
        CompassQueryBuilder queryBuilder = session.queryBuilder();
        CompassBooleanQueryBuilder boolQuery = queryBuilder.bool();
        CompassBooleanQueryBuilder manualQuery = queryBuilder.bool();
        /**
         * first query in manualBind field
         */
        StringBuffer keyBuffer = new StringBuffer();
        if (keywords != null) {
            for (String key : keywords) {
                keyBuffer.append("manualbind: ");
                keyBuffer.append(key);
                keyBuffer.append(" OR ");
            }
            log.debug("addMust: " + keyBuffer.substring(0, keyBuffer.lastIndexOf("OR")));
            manualQuery
                    .addMust(queryBuilder.queryString(keyBuffer.substring(0, keyBuffer.lastIndexOf("OR"))).toQuery());
        }
        StringBuffer typeBuffer = null;
        if (types != null) {
            typeBuffer = new StringBuffer();
            for (String type : types) {
                typeBuffer.append("type: ");
                typeBuffer.append(type);
                typeBuffer.append(" OR ");
            }
            log.debug("addMust: " + typeBuffer.substring(0, typeBuffer.lastIndexOf("OR")));
            manualQuery.addMust(queryBuilder.queryString(typeBuffer.substring(0, typeBuffer.lastIndexOf("OR")))
                    .toQuery());
        }
        StringBuffer subTypeBuffer = null;
        if (subtypes != null) {
            subTypeBuffer = new StringBuffer();
            for (String subtype : subtypes) {
                subTypeBuffer.append("subtype: ");
                subTypeBuffer.append(subtype);
                subTypeBuffer.append(" OR ");
            }
            log.debug("addMust: " + subTypeBuffer.substring(0, subTypeBuffer.lastIndexOf("OR")));
            manualQuery.addMust(queryBuilder.queryString(subTypeBuffer.substring(0, subTypeBuffer.lastIndexOf("OR")))
                    .toQuery());
        }
        CompassHits manualHits = manualQuery.toQuery().hits();
        log.debug("人工绑定结果集数量：" + manualHits.length());
        /**
         * second query in non-manualBind fields
         */
        if (fields == null) {
            log.debug("ALL fields");
            String temp = keyBuffer.substring(0, keyBuffer.lastIndexOf("OR"));
            log.debug("addMustNot: " + temp);
            boolQuery.addMustNot(queryBuilder.queryString(temp).toQuery());
            temp = temp.replaceAll("manualbind:", "");
            log.debug("addMust: " + temp);
            boolQuery.addMust(queryBuilder.queryString(temp).toQuery());
        } else {
            String temp = keyBuffer.substring(0, keyBuffer.lastIndexOf("OR"));
            StringBuffer tb = new StringBuffer();
            for (String field : fields) {
                if (!"manualbind".equals(field)) {
                    tb.append(temp.replaceAll("manualbind", field));
                    tb.append(" OR ");
                }
            }
            log.debug("addMust:" + tb.substring(0, tb.lastIndexOf("OR")));
            boolQuery.addMust(queryBuilder.queryString(tb.substring(0, tb.lastIndexOf("OR"))).toQuery());
        }
        if (typeBuffer != null) {
            log.debug("addMust: " + typeBuffer.substring(0, typeBuffer.lastIndexOf("OR")));
            boolQuery
                    .addMust(queryBuilder.queryString(typeBuffer.substring(0, typeBuffer.lastIndexOf("OR"))).toQuery());
        }
        if (subTypeBuffer != null) {
            log.debug("addMust: " + subTypeBuffer.substring(0, subTypeBuffer.lastIndexOf("OR")));
            boolQuery.addMust(queryBuilder.queryString(subTypeBuffer.substring(0, subTypeBuffer.lastIndexOf("OR")))
                    .toQuery());
        }
        CompassHits hits = boolQuery.toQuery().hits();
        log.debug("非人工绑定结果集数量：" + hits.length());
        try {
            JSONArray array = new JSONArray();
            for (int i = 0; i < manualHits.length(); ++i) {
                Info info = (Info) manualHits.data(i);
                log.debug(info.getType().getName() + "#" + info);
                JSONObject jo = new JSONObject();
                if (info instanceof Entrance) {
                    Entrance et = (Entrance) info;
                    jo = entranceToJSONObject(et);
                    array.put(jo);
                } else {
                    jo = infoToJSONObject(info);
                    array.put(jo);
                }
            }

            for (int i = 0; i < hits.length(); ++i) {

                Info info = (Info) hits.data(i);
                log.debug(info.getType().getName() + "#" + info);
                JSONObject jo = new JSONObject();
                if (info instanceof Entrance) {
                    Entrance et = (Entrance) info;
                    jo = entranceToJSONObject(et);
                    array.put(jo);
                } else {
                    jo = infoToJSONObject(info);
                    array.put(jo);
                }
            }
            result = "{\"totalCount\":\"" + (manualHits.length() + hits.length()) + "\",\"results\":"
                    + array.toString() + "}";
        } catch (JSONException e) {
            log.error(e);
        }
        return result;
    }

    public JSONObject entranceToJSONObject(Entrance et) throws JSONException {
        JSONObject jo = new JSONObject();
        jo.put("name", et.getName());
        if (et.getType() != null)
            jo.put("type", et.getType().getName());
        if (et.getSubType() != null)
            jo.put("subtype", et.getSubType().getName());
        jo.put("description", et.getDescription());
        jo.put("mediacode", "");
        jo.put("url", et.getUrl());
        return jo;
    }

    public JSONObject infoToJSONObject(Info info) throws JSONException {
        JSONObject jo = new JSONObject();
        jo.put("name", info.getName());
        if (info.getType() != null)
            jo.put("type", info.getType().getName());
        if (info.getSubType() != null)
            jo.put("subtype", info.getSubType().getName());
        jo.put("description", info.getDescription());
        jo.put("mediacode", info.getCode());
        jo.put("url", "");
        return jo;
    }

    public SystemCache getSystemCache() {
        return systemCache;
    }

    public void setSystemCache(SystemCache systemCache) {
        this.systemCache = systemCache;
    }
}
